import {Layout} from '../../src/Layout';
export default Layout;

import docs from 'docs:react-aria-components';
import DropOperation from '@react-aria/dnd/docs/DropOperation.svg';
import BetweenDropPosition from '@react-aria/dnd/docs/BetweenDropPosition.svg';
import OnDropPosition from '@react-aria/dnd/docs/OnDropPosition.svg';
import RootDropPosition from '@react-aria/dnd/docs/RootDropPosition.svg';
import Anatomy from '@react-aria/dnd/docs/Anatomy.svg';
import DragAffordance from '@react-aria/dnd/docs/DragAffordance.svg';
import {GroupedPropTable} from '../../src/PropTable';
import {PokemonListBox} from './PokemonListBox';
import {PokemonGridList} from './PokemonGridList';

export const section = 'Guides';
export const description = 'How to implement drag and drop.';

# Drag and Drop

<PageDescription>React Aria collection components support drag and drop with mouse and touch interactions, and full keyboard and screen reader accessibility. Learn how to provide drag data and handle drop events to move, insert, or reorder items.</PageDescription>

## Introduction

Drag and drop allows a user to move data between two locations. The initial location is referred to as a **drag source**, and the final location is referred to as a **drop target**. The dragged data consists of one or more **drag items**, each of which contains data such as text, files, or application-specific objects. These are shown in a **drag preview** under the user's cursor.

<Anatomy role="img" aria-label="Drag and drop anatomy diagram, showing drag source, drag preview, and drop target." />

Drag and drop is implemented using the <TypeLink links={docs.links} type={docs.exports.useDragAndDrop} /> hook. The result of this function is passed into components that support drag and drop, such as [ListBox](ListBox), [GridList](GridList), [Tree](Tree), and [Table](Table).

## Drag source

A drag source provides one or more **drag items**. Each item includes one or more data formats, e.g. JSON, HTML, or plain text. Drag types can be standard [mime types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) or application-specific strings. Providing multiple data formats allows the user to drop in external applications such as email clients or text editors.

This example provides items as plain text, HTML, and a custom app-specific data format. Dropping within this page will use the custom data format. If you drop in an external application supporting rich text, the HTML representation will be used. Dropping in a text editor will use the plain text format.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DroppableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DroppableCOMPONENT} from './DroppableCOMPONENT';

function DraggableCOMPONENT() {
  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    ///- begin highlight -///
    getItems(keys, values) {
      return values.map(item => {
        return {
          'text/plain': `${item.name} – ${item.type}`,
          'text/html': `<strong>${item.name}</strong> – <em>${item.type}</em>`,
          'pokemon': JSON.stringify(item)
        };
      });
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT dragAndDropHooks={dragAndDropHooks} />;
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

## Drag preview

While dragging, a **drag preview** is displayed under the user's mouse or finger to represent the items being dragged. By default, this is a copy of the dragged element. A custom preview can be rendered by the `renderDragPreview` function, which receives the dragged data, and returns a JSX element.

This example renders a custom drag preview which shows the number of items being dragged.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DroppableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DroppableCOMPONENT} from './DroppableCOMPONENT';

function DraggableCOMPONENT() {
  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    ///- begin collapse -///
    getItems(keys, items) {
      return items.map(item => ({
        'text/plain': item.name
      }));
    },
    ///- end collapse -///
    ///- begin highlight -///
    renderDragPreview(items) {
      return (
        <div className="drag-preview">
          {items[0]['text/plain']}
          <span className="badge">{items.length}</span>
        </div>
      );
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT dragAndDropHooks={dragAndDropHooks} />;
}
```

</ExampleSwitcher>

```css render hidden
/* TODO: move into starter */
.drag-preview {
  width: 150px;
  padding: var(--spacing-2);
  padding-inline-start: var(--spacing-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-1);
  background: var(--highlight-background);
  color: var(--highlight-foreground);
  border-radius: var(--radius);

  .badge {
    background: var(--highlight-foreground);
    color: var(--highlight-background);
    padding: 0 var(--spacing-2);
    border-radius: var(--radius-sm);
  }
}
```

## Drop items

Users can drop one or more **drop items**, each of which contains data to be transferred from the drag source to drop target. There are three kinds of drag items:

* `text` – represents data inline as a string in one or more formats
* `file` – references a file on the user's device
* `directory` – references the contents of a directory

### Text

A <TypeLink links={docs.links} type={docs.links[docs.exports.TextDropItem.id]} /> represents textual data in one or more different formats. These may be either standard [mime types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) or custom app-specific formats.

This example uses the `acceptedDragTypes` prop to accept items that include an app-specific type, which is retrieved using the item's `getText` method. When `acceptedDragTypes` is specified, the dropped items are filtered to include only items that include the accepted types.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DraggableCOMPONENT.tsx']}
"use client";
import {isTextDropItem, useDragAndDrop} from 'react-aria-components';
import {useState} from 'react';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DraggableCOMPONENT} from './DraggableCOMPONENT';

function DroppableCOMPONENT() {
  let [items, setItems] = useState<Pokemon[]>([]);

  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    ///- begin highlight -///
    acceptedDragTypes: ['pokemon'],
    async onRootDrop(e) {
      let items = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => JSON.parse(await item.getText('pokemon')))
      );
      setItems(items);
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={items} dragAndDropHooks={dragAndDropHooks} />;
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### Files

A <TypeLink links={docs.links} type={docs.links[docs.exports.FileDropItem.id]} /> references a file on the user's device. It includes the name and mime type of the file, and methods to read the contents as plain text, or retrieve a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object for uploading. This example accepts JPEG and PNG image files, and renders them by creating a local [object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL).

```tsx render
"use client";
import {GridList, GridListItem} from 'vanilla-starter/GridList';
import {useDragAndDrop, isFileDropItem, Text} from 'react-aria-components';
import {useState} from 'react';

interface ImageItem {
  id: number,
  url: string,
  name: string
}

function DroppableGridList() {
  let [items, setItems] = useState<ImageItem[]>([]);

  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin highlight -///
    acceptedDragTypes: ['image/jpeg', 'image/png'],
    async onRootDrop(e) {
      let items = await Promise.all(
        e.items.filter(isFileDropItem).map(async item => ({
          id: Math.random(),
          url: URL.createObjectURL(await item.getFile()),
          name: item.name
        }))
      );
      setItems(items);
    }
    ///- end highlight -///
  });

  return (
    <GridList
      aria-label="Droppable list"
      items={items}
      dragAndDropHooks={dragAndDropHooks}
      renderEmptyState={() => "Drop images here"}
      style={{height: 250}}
      data-size="small">
      {item => (
        <GridListItem textValue={item.name}>
          <img src={item.url} />
          <Text>{item.name}</Text>
        </GridListItem>
      )}
    </GridList>
  );
}
```

### Directories

A <TypeLink links={docs.links} type={docs.links[docs.exports.DirectoryDropItem.id]} /> references the contents of a directory on the user's device. It includes the name of the directory, as well as a method to iterate through the files and folders within the directory. Include the special `DIRECTORY_DRAG_TYPE` type in `acceptedDragTypes` to limit drops to directories.

```tsx render
"use client";
import {Tree, TreeItem} from 'vanilla-starter/Tree';
import {useDragAndDrop, DIRECTORY_DRAG_TYPE, isDirectoryDropItem, Collection} from 'react-aria-components';
import {useState} from 'react';
import File from '@react-spectrum/s2/icons/File';
import Folder from '@react-spectrum/s2/icons/Folder';

interface DirItem {
  id: number,
  name: string,
  kind: string,
  type: string,
  children?: DirItem[]
}

function DroppableTree() {
  let [files, setFiles] = useState<DirItem[]>([]);

  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin highlight -///
    acceptedDragTypes: [DIRECTORY_DRAG_TYPE],
    async onRootDrop(e) {
      // Read entries in directory and update state with relevant info.
      let getFiles = async (dir) => {
        let files = [];
        for await (let entry of dir.getEntries()) {
          files.push({
            id: Math.random(),
            name: entry.name,
            kind: entry.kind,
            children: entry.kind === 'directory' ? await getFiles(entry) : []
          });
        }
        return files;
      };

      let dir = e.items.find(isDirectoryDropItem)!;
      setFiles(await getFiles(dir));
    }
    ///- end highlight -///
  });

  return (
    <Tree
      aria-label="Droppable tree"
      items={files}
      dragAndDropHooks={dragAndDropHooks}
      renderEmptyState={() => 'Drop directory here'}
      style={{height: 250}}>
      {function renderItem(item) {
        return (
          <TreeItem
            title={
              <div style={{display: 'flex', alignItems: 'center', gap: 8}}>
                <span style={{flex: '0 0 auto'}}>{item.kind === 'directory' ? <Folder /> : <File />}</span>
                <span style={{flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'hidden'}}>{item.name}</span>
              </div>
            }>
            <Collection items={item.children}>
              {renderItem}
            </Collection>
          </TreeItem>
        );
      }}
    </Tree>
  );
}
```

## Drop positions

Collection components such as [ListBox](ListBox), [Table](Table), [Tree](Tree), and [GridList](GridList) support multiple **drop positions**.

* The `"root"` drop position allows dropping on the collection as a whole. 
* The `"on"` drop position allows dropping on individual collection items, such as a folder within a list.
* The `"before"` and `"after"` drop positions allow the user to insert or move items between other items. This is displayed by rendering a **drop indicator** between items.

<Figure>
  <div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 50, marginBottom: 4, background: 'var(--anatomy-gray-100)', padding: 32, width: 'calc(100% - 64px)', borderRadius: 'var(--anatomy-radius)'}}>
    <RootDropPosition role="img" aria-label="Root drop position" />
    <OnDropPosition role="img" aria-label="On drop position" />
    <BetweenDropPosition role="img" aria-label="Between drop position" />
  </div>
  <Caption style={{fontStyle: 'italic'}}>The "root", "on", and "between" drop positions.</Caption>
</Figure>

### Dropping on the collection

Use the `onRootDrop` event to enable dropping on the entire collection. When a valid drag hovers over the collection, it receives the `data-drop-target` state.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DraggableCOMPONENT.tsx']}
"use client";
import {isTextDropItem, useDragAndDrop} from 'react-aria-components';
import {useState} from 'react';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DraggableCOMPONENT} from './DraggableCOMPONENT';

function DroppableCOMPONENT() {
  let [items, setItems] = useState<Pokemon[]>([]);

  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    acceptedDragTypes: ['pokemon'],
    ///- begin highlight -///
    async onRootDrop(e) {
      let items = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => JSON.parse(await item.getText('pokemon')))
      );
      setItems(items);
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={items} dragAndDropHooks={dragAndDropHooks} />;
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### Dropping on items

Use the `onItemDrop` event to enable dropping on items. When a valid drag hovers over an item, it receives the `data-drop-target` state.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DraggableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DraggableCOMPONENT} from './DraggableCOMPONENT';

function DroppableCOMPONENT() {
  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin highlight -///
    onItemDrop(e) {
      alert(`Dropped on ${e.target.key}`);
    }
    ///- end highlight -///
  });

  return (
    <PokemonCOMPONENT
      dragAndDropHooks={dragAndDropHooks}
      items={[
        {id: 1, name: 'Beedrill', type: 'Bug, Poison', level: 25},
        {id: 2, name: 'Pidgeot', type: 'Flying', level: 40},
        {id: 3, name: 'Fearow', type: 'Flying', level: 32},
        {id: 4, name: 'Jigglypuff', type: 'Fairy', level: 56}
      ]} />
  );
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### Dropping between items

Use the `onInsert` event to enable dropping between items. React Aria renders a <TypeLink links={docs.links} type={docs.exports.DropIndicator} /> between items to indicate the insertion position, which can be customized using `renderDropIndicator`.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DraggableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop, isTextDropItem, DropIndicator, useListData} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DraggableCOMPONENT} from './DraggableCOMPONENT';

function DroppableCOMPONENT() {
  let list = useListData({
    initialItems: [
      {id: 1, name: 'Beedrill', type: 'Bug, Poison', level: 25},
      {id: 2, name: 'Pidgeot', type: 'Flying', level: 40},
      {id: 3, name: 'Fearow', type: 'Flying', level: 32},
      {id: 4, name: 'Jigglypuff', type: 'Fairy', level: 56}
    ]
  });

  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin highlight -///
    async onInsert(e) {
      let items = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => {
            let pokemon = JSON.parse(await item.getText('pokemon'));
            let processItem = item => ({
              ...item,
              id: Math.random(),
              children: item.children?.map(processItem)
            });
            return processItem(pokemon);
          })
      );

      if (e.target.dropPosition === 'before') {
        list.insertBefore(e.target.key, ...items);
      } else if (e.target.dropPosition === 'after') {
        list.insertAfter(e.target.key, ...items);
      }
    },
    renderDropIndicator(target) {
      return <DropIndicator target={target} />;
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={list.items} dragAndDropHooks={dragAndDropHooks} />;
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### Reordering items

Use the `onReorder` event to enable reordering items. For components with hierarchy like [Tree](Tree), this only allows reordering within the same level. Use `onMove` to allow moving items between levels.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx']}
"use client";
import {useDragAndDrop, useListData} from 'react-aria-components';
import {PokemonCOMPONENT, defaultItems} from './PokemonCOMPONENT';

function ReorderableCOMPONENT() {
  let list = useListData({
    initialItems: defaultItems
  });

  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin collapse -///
    getItems(keys, items) {
      return items.map(item => {
        return {
          'text/plain': `${item.name} – ${item.type}`,
          'text/html': `<strong>${item.name}</strong> – <em>${item.type}</em>`,
          'pokemon': JSON.stringify(item)
        };
      });
    },
    ///- end collapse -///
    ///- begin highlight -///
    onReorder(e) {
      if (e.target.dropPosition === 'before') {
        list.moveBefore(e.target.key, e.keys);
      } else if (e.target.dropPosition === 'after') {
        list.moveAfter(e.target.key, e.keys);
      }
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={list.items} dragAndDropHooks={dragAndDropHooks} />
}
```

</ExampleSwitcher>

### Moving items

Use the `onMove` event to enable moving items within a collection. This allows reordering items within a level and moving items between levels. It supports dropping both on and between items.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx']}
"use client";
import {useDragAndDrop, useTreeData} from 'react-aria-components';
import {PokemonCOMPONENT, defaultItems} from './PokemonCOMPONENT';

function ReorderableCOMPONENT() {
  let tree = useTreeData({
    initialItems: defaultItems
  });

  let {dragAndDropHooks} = useDragAndDrop({
    ///- begin collapse -///
    getItems(keys, items) {
      return items.map(item => {
        return {
          'text/plain': `${item.name} – ${item.type}`,
          'text/html': `<strong>${item.name}</strong> – <em>${item.type}</em>`,
          'pokemon': JSON.stringify(item)
        };
      });
    },
    ///- end collapse -///
    ///- begin highlight -///
    onMove(e) {
      if (e.target.dropPosition === 'before') {
        tree.moveBefore(e.target.key, e.keys);
      } else if (e.target.dropPosition === 'after') {
        tree.moveAfter(e.target.key, e.keys);
      } else if (e.target.dropPosition === 'on') {
        // Move items to become children of the target
        let targetNode = tree.getItem(e.target.key);
        if (targetNode) {
          let targetIndex = targetNode.children
            ? targetNode.children.length
            : 0;
          let keyArray = Array.from(e.keys);
          for (let i = 0; i < keyArray.length; i++) {
            tree.move(keyArray[i], e.target.key, targetIndex + i);
          }
        }
      }
    }
    ///- end highlight -///
  });

  // Map tree items to Pokemon objects
  let processItem = item => {
    return {...item.value, children: item.children.map(processItem)}
  };

  let items = tree.items.map(processItem);
  return <PokemonCOMPONENT items={items} dragAndDropHooks={dragAndDropHooks} />
}
```

</ExampleSwitcher>

### Multiple positions

This example puts together many of the examples described above, allowing users to drag items between lists bidirectionally. It also supports reordering items within the same list. When a list is empty, it accepts drops on the whole collection.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render
"use client";
import {useDragAndDrop, isTextDropItem, useListData} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon, defaultItems} from './PokemonCOMPONENT';

interface DndCOMPONENTProps {
  initialItems: Pokemon[],
  'aria-label': string
}

function DndCOMPONENT(props: DndCOMPONENTProps) {
  let list = useListData({
    initialItems: props.initialItems
  });

  let {dragAndDropHooks} = useDragAndDrop({
    renderDragPreview(items) {
      return (
        <div className="drag-preview">
          {items[0]['text/plain']}
          <span className="badge">{items.length}</span>
        </div>
      );
    },
    // Provide drag data in a custom format as well as plain text.
    getItems(keys) {
      return [...keys].map((key) => {
        let item = list.getItem(key);
        return {
          'pokemon': JSON.stringify(item),
          'text/plain': item.name
        };
      });
    },

    // Accept drops with the custom format.
    acceptedDragTypes: ['pokemon'],

    // Ensure items are always moved rather than copied.
    getDropOperation: () => 'move',

    ///- begin focus -///
    // Handle drops between items from other lists.
    async onInsert(e) {
      let processedItems = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => JSON.parse(await item.getText('pokemon')))
      );
      if (e.target.dropPosition === 'before') {
        list.insertBefore(e.target.key, ...processedItems);
      } else if (e.target.dropPosition === 'after') {
        list.insertAfter(e.target.key, ...processedItems);
      }
    },

    // Handle drops on the collection when empty.
    async onRootDrop(e) {
      let processedItems = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => JSON.parse(await item.getText('pokemon')))
      );
      list.append(...processedItems);
    },

    // Handle reordering items within the same list.
    onReorder(e) {
      if (e.target.dropPosition === 'before') {
        list.moveBefore(e.target.key, e.keys);
      } else if (e.target.dropPosition === 'after') {
        list.moveAfter(e.target.key, e.keys);
      }
    },

    // Remove the items from the source list on drop
    // if they were moved to a different list.
    onDragEnd(e) {
      if (e.dropOperation === 'move' && !e.isInternal) {
        list.remove(...e.keys);
      }
    }
  });
  ///- end focus -///

  return <PokemonCOMPONENT items={list.items} dragAndDropHooks={dragAndDropHooks} />
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DndCOMPONENT
    initialItems={defaultItems}
    aria-label="Drag and drop COMPONENT" />
  <DndCOMPONENT
    initialItems={[]}
    aria-label="Drag and drop COMPONENT" />
</div>
```

</ExampleSwitcher>

## Drop operations

A <TypeLink links={docs.links} type={docs.links[docs.exports.DropOperation.id]} /> is an indication of what will happen when dragged data is dropped on a particular drop target. These are:

* `move` – the dragged data will be moved from its source location to the target location.
* `copy` – the dragged data will be copied to the target destination.
* `link` – a relationship will be established between the source and target locations.
* `cancel` – the drag and drop operation will be canceled, resulting in no changes made to the source or target.

Many operating systems display these in the form of a cursor change, e.g. a plus sign to indicate a copy operation. The user may also be able to use a modifier key to choose which drop operation to perform, such as <Keyboard>Option</Keyboard> or <Keyboard>Alt</Keyboard> to switch from move to copy.

<Figure>
  <DropOperation role="img" aria-labelledby="drop-operation-caption" />
  <Caption id="drop-operation-caption" style={{fontStyle: 'italic'}}>Visual feedback for a copy drop operation.</Caption>
</Figure>


### getDropOperation

Use `getDropOperation` option to provide feedback to the user when a drag hovers over the drop target. This function receives the drop target, set of types contained in the drag, and a list of allowed drop operations. It should return the operation that will be performed on drop, or `'cancel'` to reject the drop. If the returned operation is not in `allowedOperations`, the drop will be canceled.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DraggableCOMPONENT.tsx']}
"use client";
import {isTextDropItem, useDragAndDrop} from 'react-aria-components';
import {useState} from 'react';
import {PokemonCOMPONENT, Pokemon} from './PokemonCOMPONENT';
import {DraggableCOMPONENT} from './DraggableCOMPONENT';

function DroppableCOMPONENT() {
  let [items, setItems] = useState<Pokemon[]>([]);

  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    acceptedDragTypes: ['pokemon'],
    ///- begin highlight -///
    getDropOperation: (target, types, allowedOperations) => 'copy',
    ///- end highlight -///
    async onRootDrop(e) {
      let items = await Promise.all(
        e.items
          .filter(isTextDropItem)
          .map(async item => JSON.parse(await item.getText('pokemon')))
      );
      setItems(items);
    }
  });

  return <PokemonCOMPONENT items={items} dragAndDropHooks={dragAndDropHooks} />;
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### getAllowedDropOperations

The drag source can also control which drop operations are allowed. In the example below, the cursor shows the copy cursor by default, and pressing a modifier key cancels the drop.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DroppableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop, useListData} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon, defaultItems} from './PokemonCOMPONENT';
import {DroppableCOMPONENT} from './DroppableCOMPONENT';

function DraggableCOMPONENT() {
  ///- begin collapse -///
  let list = useListData({
    initialItems: defaultItems
  });
  ///- end collapse -///

  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    ///- begin collapse -///
    getItems(keys, items) {
      return items.map(item => {
        return {
          'text/plain': `${item.name} – ${item.type}`,
          'text/html': `<strong>${item.name}</strong> – <em>${item.type}</em>`,
          'pokemon': JSON.stringify(item)
        };
      });
    },
    ///- end collapse -///
    ///- begin highlight -///
    getAllowedDropOperations: () => ['copy']
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={list.items} dragAndDropHooks={dragAndDropHooks} />
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### onDragEnd

The `onDragEnd` event allows the drag source to respond when a drag that it initiated ends, either because it was dropped or because it was canceled by the user. The `dropOperation` property of the event object indicates the operation that was performed. For example, when data is moved, the UI could be updated to reflect this change by removing the original dragged items.

This example removes the dragged items from the UI when a move operation is completed. Try holding the <Keyboard>Option</Keyboard> or <Keyboard>Alt</Keyboard> keys to change the operation to copy, and see how the behavior changes.

<ExampleSwitcher type="component" examples={['ListBox', 'GridList', 'Tree', 'Table']}>

```tsx render files={['packages/dev/s2-docs/pages/react-aria/PokemonCOMPONENT.tsx', 'packages/dev/s2-docs/pages/react-aria/DroppableCOMPONENT.tsx']}
"use client";
import {useDragAndDrop, useListData} from 'react-aria-components';
import {PokemonCOMPONENT, Pokemon, defaultItems} from './PokemonCOMPONENT';
import {DroppableCOMPONENT} from './DroppableCOMPONENT';

function DraggableCOMPONENT() {
  ///- begin collapse -///
  let list = useListData({
    initialItems: defaultItems
  });
  ///- end collapse -///

  let {dragAndDropHooks} = useDragAndDrop<Pokemon>({
    ///- begin collapse -///
    getItems(keys, items) {
      return items.map(item => {
        return {
          'text/plain': `${item.name} – ${item.type}`,
          'text/html': `<strong>${item.name}</strong> – <em>${item.type}</em>`,
          'pokemon': JSON.stringify(item)
        };
      });
    },
    ///- end collapse -///
    ///- begin highlight -///
    onDragEnd(e) {
      if (e.dropOperation === 'move') {
        list.remove(...e.keys);
      }
    }
    ///- end highlight -///
  });

  return <PokemonCOMPONENT items={list.items} dragAndDropHooks={dragAndDropHooks} />
}

<div style={{display: 'flex', gap: 12, flexWrap: 'wrap', width: '100%', justifyContent: 'center'}}>
  <DraggableCOMPONENT />
  <DroppableCOMPONENT />
</div>
```

</ExampleSwitcher>

### Drop events

Drop events such as `onInsert`, `onItemDrop`, etc. also include the `dropOperation`. This can be used to perform different actions accordingly, for example, when communicating with a backend API.

```tsx
let onItemDrop = async (e) => {
  let data = JSON.parse(await e.items[0].getText('my-app-file'));
  switch (e.dropOperation) {
    case 'move':
      MyAppFileService.move(data.filePath, props.filePath);
      break;
    case 'copy':
      MyAppFileService.copy(data.filePath, props.filePath);
      break;
    case 'link':
      MyAppFileService.link(data.filePath, props.filePath);
      break;
  }
};
```

## Accessibility

While drag and drop has historically been mostly limited to mouse and touchscreen users, keyboard and screen reader friendly alternatives are important for users who cannot use these interaction methods. React Aria implements keyboard and screen reader interactions for drag and drop that provide full parity with the mouse and touch experiences.

Users can press <Keyboard>Enter</Keyboard> on a draggable element to enter drag and drop mode. Then, they can press <Keyboard>Tab</Keyboard> to cycle between the drop targets that accept the dragged data, and <Keyboard>Enter</Keyboard> to drop. <Keyboard>Escape</Keyboard> cancels a drag. Touch screen reader users can also drag by double tapping to activate drag and drop mode, swiping between drop targets, and double tapping again to drop. Screen reader announcements are included to help guide the user through this process.

Collection components such as [GridList](GridList) and [Table](Table) are treated as a single drop target, so that users can easily tab past them to get to the next drop target. Within a droppable collection, keys such as <Keyboard>ArrowDown</Keyboard> and <Keyboard>ArrowUp</Keyboard> can be used to select a **drop position**, such as on an item, or between items.

Draggable elements can sometimes have conflicting keyboard interactions, such as selection. These are handled by adding an explicit **drag affordance**. Keyboard and screen reader users can focus this element, and use it to initiate drag and drop for the parent item. In addition, this has the added benefit of making drag and drop more discoverable.

<Figure>
  <DragAffordance role="img" aria-labelledby="drag-affordance-caption" />
  <Caption id="drag-affordance-caption" style={{fontStyle: 'italic'}}>A focusable drag affordance to initiate keyboard and screen reader drag and drop.</Caption>
</Figure>

Note that because mouse and touch drag and drop interactions utilize the native browser APIs, they work both within the browser window and with external applications on the user's device. Keyboard and screen reader drag and drop is implemented from scratch, and therefore can only be supported within the browser window. Alternative interactions for operations involving external applications, such as file uploading or copy and paste, should be implemented in addition to drag and drop.

## Examples

<ExampleList tag="drag and drop" pages={props.pages} />

## API

### useDragAndDrop

<GroupedPropTable
  properties={docs.exports.DragAndDropOptions.properties}
  links={docs.links}
  propGroups={{
    'Drag source': [
      'getItems',
      'renderDragPreview',
      'getAllowedDropOperations',
      'onDragStart',
      'onDragMove',
      'onDragEnd'
    ],
    'Drop target': [
      'onRootDrop',
      'onItemDrop',
      'onInsert',
      'onReorder',
      'onMove',
      'onDropEnter',
      'onDropExit',
      'onDropActivate',
      'onDrop',
      'renderDropIndicator',
      'acceptedDragTypes',
      'shouldAcceptItemDrop',
      'getDropOperation',
      'dropTargetDelegate'
    ]
  }}
  defaultExpanded={new Set(['Drag source', 'Drop target'])} />

### DropIndicator

<PropTable component={docs.exports.DropIndicator} links={docs.links} />
